﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LambdaProj
{
    public class ThreadHelper
    {
        static internal Thread[] threads = new Thread[10];
        public static void Run()
        {
            Account acc = new Account(0);
            for(int i=0;i<10;i++)
            {
                Thread t = new Thread(new ThreadStart(acc.DoTrancations));
                threads[i] = t;
            }

            for(int i=0;i<10;i++)
            {
                threads[i].Name = i.ToString();
            }

            for(int i=0;i<10;i++)
            {
                threads[i].Start();
            }
            Console.ReadLine();
        }
    }

    public class Alpha
    {
        public void Beta()
        {
            while(true)
            {
                Console.WriteLine("Alpha.Beta is running in its own thread.");
            }
        }
    }

    public class ThreadSample
    {
        public static int Run()
        {
            Console.WriteLine("Thread start/stop/join sample");
            Alpha oAlpha = new Alpha();
            Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));
            oThread.Start();
            while(oThread.IsAlive)
            {
                Thread.Sleep(1);
            }
            oThread.Abort();
            oThread.Join();
            Console.WriteLine("Alpha.Beta has finished!!");
            try
            {
                Console.WriteLine("try to restart the alpha.Beta thread!");
            }
            catch(ThreadStartException ex)
            {
              
            }
            return 0;
        }
    }

    public class Account
    {
        int balance;
        Random r = new Random();

        internal Account(int initial)
        {
            this.balance = initial;
        }

        internal int Withdraw(int amount)
        {
            if(this.balance<0)
            {
                throw new Exception("negative balance!");
            }

            lock(this)
            {
                Console.WriteLine("current thread:"+Thread.CurrentThread.Name);
                if(balance>=amount)
                {
                    Thread.Sleep(5);
                    balance = balance - amount;
                    return amount;
                }
                else
                {
                    return 0;
                }
            }
        }

        internal void DoTrancations()
        {
            for(int i=0;i<100;i++)
            {
                Withdraw(r.Next(-50,100));
            }
        }
    }

    public class Cell
    {
        int cellContents;
        bool readerFlag = false;
        public int ReadFromCell()
        {
            lock(this)
            {
                if(!readerFlag)
                {
                    try
                    {
                        Monitor.Wait(this);
                    }
                    catch(SynchronizationLockException ex)
                    {
                        Console.WriteLine(ex);
                    }
                    catch(ThreadInterruptedException ex)
                    {
                        Console.WriteLine(ex);
                    }
                }
                Console.WriteLine("Consume :{0}", cellContents);
                readerFlag = false;
                Monitor.Pulse(this);
            }
            
            return cellContents;
        }

        public void WritetoCell(int n)
        {
            lock(this)
            {
                if(readerFlag)
                {
                    try
                    {
                        Monitor.Wait(this);
                    }
                    catch(SynchronizationLockException ex)
                    {
                        Console.WriteLine(ex);
                    }
                    catch(ThreadInterruptedException ex)
                    {
                        Console.WriteLine(ex);
                    }
                }
                cellContents = n;
                Console.WriteLine("Produce:{0}",cellContents);
                readerFlag = false;
                Monitor.Pulse(this);
            }
        }
    }

    public class CellProd
    {
        Cell cell;
        int quantity = 1;
        public CellProd(Cell box,int request)
        {
            cell = box;
            quantity = request;
        }

        public void ThreadRun()
        {
            for(int looper = 1;looper<=quantity;looper++)
            {
                cell.WritetoCell(looper);
            }
        }
    }

    public class CellCons
    {
        Cell cell;
        int quantity = 1;

        public CellCons(Cell box,int request)
        {
            cell = box;
            quantity = request;
        }

        public void ThreadRun()
        {
            int valReturned;
            for(int looper=1;looper<=quantity;looper++)
            {
                valReturned = cell.ReadFromCell();
            }
        }
    }

    public class MonitorSample
    {
        public static void Run()
        {
            int result = 0;
            Cell cell = new Cell();

            CellProd prod = new CellProd(cell,20);
            CellCons cons = new CellCons(cell,20);

            Thread producer = new Thread(new ThreadStart(prod.ThreadRun));
            Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));

            try
            {
                producer.Start();
                consumer.Start();

                producer.Join();
                consumer.Join();
                Console.ReadLine();
            }
            catch(ThreadStartException ex)
            {
                Console.WriteLine(ex);
                result = -1;
            }
            catch(ThreadInterruptedException ex)
            {
                Console.WriteLine(ex);
                result = -1;
            }
            Environment.ExitCode = result;
        }
    }
}
